package com.ohuang.apkpatch

import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.lang.StringBuilder

/**
 * 对比差分
 */
fun copyDifferenceSmali(oldFile: String, newFile: String, outFile: String) {
    var map = HashMap<String, String>()
    smali_classForeach(oldFile) {
        findSmali(it.absolutePath, "", map)
    }


    var smaliFile = File("$newFile\\smali")
    if (smaliFile.exists()) {
        val outSmali = File("$outFile\\smali")
        outSmali.mkdirs()
        findAndCopyDifferentSmali(smaliFile.absolutePath, "", map, outSmali.absolutePath)
    }
    var p = 1
    while (true) {
        p++
        smaliFile = File("$newFile\\smali_classes$p")
        val outSmali = File("$outFile\\smali_classes$p")
        outSmali.mkdirs()
        if (smaliFile.exists()) {
            findAndCopyDifferentSmali(smaliFile.absolutePath, "", map, outSmali.absolutePath)
        } else {
            break
        }
    }


}

/**
 *  比较并复制内容不同smali
 */
fun findAndCopyDifferentSmali(rootPath: String, path: String, map: MutableMap<String, String>, outRootPath: String) {
    val thisPath = rootPath + path
    val thisOutPath = outRootPath + path
    val file = File(thisPath)
    if (file.exists()) {
        if (file.isFile) {
            if (!map.containsKey(path)) {
                copyFile(thisPath, thisOutPath, true)
            } else {
                copyDifferenceFile(File(map[path]!!), File(thisPath), File(thisOutPath))
            }
        } else {
            file.listFiles()?.forEach {
                val key = path + "/" + it.name
                if (it.isFile) {
                    if (!map.containsKey(key)) {
                        copyFile(it.absolutePath, outRootPath + key, true)
                    } else {
                        copyDifferenceFile(File(map[key]!!), File(it.absolutePath), File(outRootPath + key))
                    }
                } else {
                    findAndCopyDifferentSmali(rootPath, key, map, outRootPath)
                }
            }
        }
    }
}

/**
 * 遍历所有smali  并根据<包名路径,文件真实路径>保存到map
 */
fun findSmali(rootPath: String, path: String, map: MutableMap<String, String>) {
    val thisPath = rootPath + path
    var file = File(thisPath)
    if (file.exists()) {
        if (file.isFile) {
            if (!map.containsKey(path)) {
                map[path] = file.absolutePath
            }
        } else {
            file.listFiles()?.forEach {
                var key = path + "/" + it.name
                if (it.isFile) {
                    if (!map.containsKey(key)) {
                        map[key] = it.absolutePath
                    }
                } else {
                    findSmali(rootPath, key, map)
                }

            }
        }
    }

}

/**
 * 删除R.smali
 */
fun deleteRSmali(path: String) {

    var file = File(path)
    val data = ArrayList<String>()

    if (file.isDirectory) {
        searchFileInPath(file.absolutePath, "R.smali", data)
    } else if ("R.smali" == file.name) {
        data.add(file.absolutePath)
    }

    data.forEach { itS ->
        println("----开始删除$itS-----")
        var file1 = File(itS)
        var parentFile = file1.parentFile
        parentFile.listFiles()?.forEach { itF ->
            if (itF.isFile) {
                if (itF.name.startsWith("R\$")) {
                    var delete = itF.delete()
                    if (delete) {
                        println("删除文件" + itF.absolutePath)
                    } else {
                        println("删除文件失败" + itF.absolutePath)
                    }
                } else if (itF.name.equals("R.smali")) {
                    var delete = itF.delete()
                    if (delete) {
                        println("删除文件" + itF.absolutePath)
                    } else {
                        println("删除文件失败" + itF.absolutePath)
                    }
                }
            }
        }
    }
}


/**
 * 修改public.xml 后更新R.smali
 */
fun updateRSmali2(public: String, path: String) {
    var readPublic = readPublic(public)
    var file = File(path)
    val data = ArrayList<String>()

    if (file.isDirectory) {
        searchFileInPath(file.absolutePath, "R.smali", data)
    } else if ("R.smali" == file.name) {
        data.add(file.absolutePath)
    }

    data.forEach { itS ->
        println("----开始修改" + itS + "-----")
        var file1 = File(itS)
        var parentFile = file1.parentFile
        parentFile.listFiles()?.forEach { itF ->
            if (itF.isFile) {
                if (itF.name.startsWith("R\$")) {
                    var name = StringBuilder().append(itF.name).toString()
                    var key = name.replace("R\$", "").replace(".smali", "")
                    var hashMap = readPublic[key]
                    if (hashMap != null) {
                        changeRSmali(itF.absolutePath, hashMap)
                    }
                }
            }
        }
    }
}


/**
 * 改变R.smali的值
 */
fun changeRSmali(path: String, map: HashMap<String, String>) {

    var fileReader = FileReader(path)

    var sb = StringBuilder()


    var readLines = fileReader.readLines()
    readLines.forEach {
        if (it.startsWith(".field public static final")) {
            var s = StringBuilder().append(it).toString().replace(".field public static final", "")
            var split = s.split(":I =")
            if (split.size != 2) {
                sb.append(it)
                sb.append("\n")
                return@forEach
            }
            var name = split[0].replace(" ", "")
            if (map.containsKey(name)) {
                var id = map[name]
                if (id != null && id!!.isNotEmpty()) {
                    var sp = StringBuilder().append(it).toString().replace(split[1].replace(" ", ""), id)
                    sb.append(sp)
                } else {
                    sb.append(it)
                }
            } else {
                sb.append(it)
            }

        } else {
            sb.append(it)
        }
        sb.append("\n")
    }


    var fileWriter = FileWriter(path)
    // 把替换完成的字符串写入文件内
    fileWriter.write(sb.toString().toCharArray());
    // 关闭文件流，释放资源
    fileReader.close()
    fileWriter.close()

}


/**
 * 复制指定包名的smali
 *
 *     arrayList.add("/aa/bb/cc")
 */
fun copySmaliForPackage(rootPath: String, outPath: String, pages: List<String>) {
    var file = File(rootPath)
    var mutableMap = HashMap<String, String>()
    smali_classForeach(rootPath) {
        findSmali(it.absolutePath, "", mutableMap)
    }
    fun isMatch(string: String):Boolean {
        pages.forEach {
            if (string.startsWith(it)){
                return true
            }
        }
        return false
    }
    mutableMap.forEach { (t, u) ->
        if (isMatch(t)) {
            File(u).copyTo(File("$outPath$t"))
            println("t=$t  u=$u")
        }
    }
}

fun deleteSmaliForPackage(rootPath: String, retain: List<String>){
    var file = File(rootPath)
    var mutableMap = HashMap<String, String>()
    smali_classForeach(rootPath) {
        findSmali(it.absolutePath, "", mutableMap)
    }
    fun isMatch(string: String):Boolean {
        retain.forEach {
            if (string.startsWith(it)){
                return true
            }
        }
        return false
    }
    mutableMap.forEach { (t, u) ->
        if (isMatch(t)) {
            println("t=$t  u=$u")
        }else{
            File(u).delete()
        }
    }
}


fun smali_classForeach(rootPath: String, lmb: (file: File) -> Unit) {
    val file = File("$rootPath/smali")
    if (file.exists() && file.isDirectory) {
        lmb.invoke(file)
    }
    for (i in 2..100) {
        val file0 = File("$rootPath/smali_classes$i")
        if (file0.exists() && file0.isDirectory) {
            lmb.invoke(file0)
        }
    }
}

/**
 * 复制smali并替换一些包名  并进行包名替换(用于解决第三方库冲突问题)
 *
 * replaceMap  替换规则 key为原包名  value为修改后的包名
 */
fun copyAndChangePackageName(path: String, path1: String, replaceMap: Map<String, String>) {
    var file = File(path)
    var file1 = File(path1)

    file.listFiles()?.forEach {
        if (it.isDirectory) {

            if (it.name.startsWith("smali")) {
                println("------开始复制${it.name} ------")
                copyAndChangePnSmali(it.absolutePath, path1 + "/" + it.name, replaceMap = replaceMap)
            }
        }

    }


}

/**
 * 复制+替换包名
 */
fun copyAndChangePnSmali(
    sourceRootPath: String,
    targetRootPath: String,
    path: String = "",
    path1: String = "",
    replaceMap: Map<String, String>

) {
    var targetFile = File(targetRootPath + path1)
    var sourceRootFile = File(sourceRootPath + path)
    if (!targetFile.exists()) {
        targetFile.mkdirs()
    }
    sourceRootFile.listFiles()?.forEach {
        if (it.isDirectory) {
            var newPath0 = path + "/${it.name}"
            val newPath2 = path1 + "/${it.name}"
            val key=newPath2.subSequence(1,newPath2.length)
            if (replaceMap.containsKey(key)) {
                val newPath2 = newPath2.replace(key.toString(), replaceMap[key]!!)
                println("$key->${replaceMap[key]}")
                copyAndChangePnSmali(sourceRootPath, targetRootPath, newPath0, newPath2, replaceMap)
            } else {
                copyAndChangePnSmali(sourceRootPath, targetRootPath, newPath0, newPath2, replaceMap)
            }

        } else {
            copyAndChangeSmaliFile(it.absolutePath, targetRootPath + path1 + "/" + it.name, replaceMap)
        }
    }
}

/**
 *  内容替换
 */
fun copyAndChangeSmaliFile(sourcePath: String, targetPath: String, replaceMap: Map<String, String>) {
    var fileReader = FileReader(sourcePath)

    var sb = StringBuilder()


    var readLines = fileReader.readLines()
    readLines.forEach {
        var stringBuilder = StringBuilder(it)
        replaceMap.forEach { (t, u) ->
            var replace = stringBuilder.toString().replace(t, u)
            stringBuilder = StringBuilder(replace)
        }
        sb.append(stringBuilder).append("\n")

    }


    var fileWriter = FileWriter(targetPath)
    // 把替换完成的字符串写入文件内
    fileWriter.write(sb.toString().toCharArray());
    // 关闭文件流，释放资源
    fileReader.close()
    fileWriter.close()
}